import java.util.Stack;


public class RuleOperator {

	Stack<ControlElement> control ;
	Stack<String> stack ;
	Environment environment;
	
	public RuleOperator(Stack<ControlElement> control, Stack<String> stack, Environment environment){
		this.control = control;
		this.stack = stack;
		this.environment = environment;
	}
	
	public static void main(String[] args) {
		Stack<ControlElement> control = new Stack<ControlElement>();
		Stack<String> stack = new Stack<String>();
		Environment environment = new Environment();
		RuleOperator ruleOP = new RuleOperator(control, stack, environment);

		// test1 
		control.push(new ControlElement("aug"));
		//stack.push("<INT:2>");
		stack.push("tau::2::3::4");
		stack.push("tau::1::7");
		System.out.println(ruleOP.checkRule());
		//ruleOP.devide();
		//ruleOP.power();
		ruleOP.aug();
		System.out.println(ruleOP.control + " " +ruleOP.stack);
		
		/*
		// test2 
		control.push(new ControlElement("-"));
		stack.push("<INT:2>");
		stack.push("<INT:2>");
		System.out.println(ruleOP.checkRule());
		ruleOP.applyRule();
		System.out.println(ruleOP.control + " " +ruleOP.stack);
		
		// test1 
		control.push(new ControlElement("+"));
		stack.push("<INT:2>");
		stack.push("<INT:2>");
		System.out.println(ruleOP.checkRule());
		ruleOP.applyRule();
		System.out.println(ruleOP.control + " " +ruleOP.stack);
		*/
	}
	
	/*
	 * Name -> Object
	 */
	boolean checkRule() {
		String controlTop = control.peek().content;
		if (controlTop.equals("+") |
				controlTop.equals("-") |
				controlTop.equals("*") |
				controlTop.equals("/") |
				controlTop.equals("**") |
				controlTop.equals("@") |
				controlTop.equals("aug") |
				controlTop.equals("or") |
				controlTop.equals("&") |
				controlTop.equals("neg") |
				controlTop.equals("not") |
				controlTop.equals("gr") |
				controlTop.equals("ge") |
				controlTop.equals("ls") |
				controlTop.equals("le") |
				controlTop.equals("eq") |
				controlTop.equals("ne") 				
			) {
			return true;
		} else {
			return false;
		}
	}
	
	void applyRule(){
		String controlTop = control.pop().content;
		if (controlTop.equals("+")){
			plus();
		}else if (controlTop.equals("-")){
			minus();
		}else if (controlTop.equals("*")){
			multiply();
		}else if (controlTop.equals("/")){
			devide();
		}else if (controlTop.equals("neg")){
			neg();
		}else if (controlTop.equals("**")){
			power();
		}else if (controlTop.equals("@")){
			infix();
		}else if (controlTop.equals("eq")){
			equal();
		}else if (controlTop.equals("ne")){
			notEqual();
		}else if (controlTop.equals("ls")){
			lessThan();
		}else if (controlTop.equals("le")){
			lessThanEqual();
		}else if (controlTop.equals("gr")){
			greaterThan();
		}else if (controlTop.equals("ge")){
			greaterThanEqual();
		}else if (controlTop.equals("not")){
			not();
		}else if (controlTop.equals("aug")){
			aug();
		}else if (controlTop.equals("or")){
			or();
		}else if (controlTop.equals("&")){
			and();
		}else {
			System.out.println("ruleOP: this state should not be reached");
			System.exit(0);
		}
		
	}

	private void and() {
		String op1 = stack.pop();
		String op2 = stack.pop();
		if (checkBoolean(op1) && checkBoolean(op2)){
			Boolean result = getBoolean(op1) && getBoolean(op2);
			stack.push("<"+result+">");
		}else{
			System.out.println("and: boolean operands expected");
		}
	}

	private void or() {
		String op1 = stack.pop();
		String op2 = stack.pop();
		if (checkBoolean(op1) && checkBoolean(op2)){
			Boolean result = getBoolean(op1) || getBoolean(op2);
			stack.push("<"+result+">");
		}else{
			System.out.println("or: boolean operands expected");
		}
	}

	private void not() {
		String op1 = stack.pop();
		if (checkBoolean(op1) && !checkNilDummy(op1)){
			String result;
			if (op1.equals("<true>")){
				result = "<false>";
			}else{
				result =  "<true>";
			}
			stack.push(result);
		}else{
			System.out.println("not: boolean operands expected");
		}
	}
	
	private boolean checkNilDummy(String op) {
		if (op.equals("<nil>") | op.equals("<dummy>")){
			return true;
		}else{
			return false;
		}
	}

	private boolean getBoolean(String pop) {
		if (pop.equals("<true>")){
			return true;
		}else{
			return false;
		}
	}
	
	private boolean checkBoolean(String pop) {
		if (pop.equals("<true>") | pop.equals("<false>")){
			return true;
		}else{
			return false;
		}
	}

	/*
	 * if equal tuples
	 */
	private void greaterThanEqual() {
		String op1 = stack.pop();
		String op2 = stack.pop();
		
		if ( checkNilDummy(op1) || checkNilDummy(op2) || 
				checkBoolean(op1) || checkBoolean(op2)){
			System.out.println("ge: illegal arguments");
		}
		
		if ( op1.equals(op2)){
			stack.push("<true>");
			return;
		}else if (op1.contains("tau::") | op2.contains("tau::") |
				op1.contains("STR:") | op2.contains("STR:") ){
			stack.push("<false>");
			return;
		}
		
		if (checkInt(op1) && checkInt(op2)){
			boolean result = getInt(op1) >= getInt(op2);
			stack.push("<"+result+">");
		}else{
			System.out.println("ge: state should not be reached");
		}
	}

	private void greaterThan() {
		String op1 = stack.pop();
		String op2 = stack.pop();
		
		if ( checkNilDummy(op1) || checkNilDummy(op2)){
			System.out.println(">=: illegal arguments nil or dummy");
		}
		
		if (op1.contains("tau::") | op2.contains("tau::") |
				op1.contains("STR:") | op2.contains("STR:") ){
			stack.push("<false>");
			return;
		}
		
		if (checkInt(op1) && checkInt(op2)){
			boolean result = getInt(op1) > getInt(op2);
			stack.push("<"+result+">");
		}else{
			System.out.println("gr: state should not be reached");
		}
	}

	private void lessThanEqual() {
		String op1 = stack.pop();
		String op2 = stack.pop();
		
		if ( checkNilDummy(op1) || checkNilDummy(op2) || 
				checkBoolean(op1) || checkBoolean(op2)){
			System.out.println("le: illegal arguments");
		}
		
		if ( op1.equals(op2)){
			stack.push("<true>");
			return;
		}else if (op1.contains("tau::") | op2.contains("tau::") |
				op1.contains("STR:") | op2.contains("STR:") ){
			stack.push("<false>");
			return;
		}
		
		if (checkInt(op1) && checkInt(op2)){
			boolean result = getInt(op1) <= getInt(op2);
			stack.push("<"+result+">");
		}else{
			System.out.println("le: state should not be reached");
		}
	}

	private void lessThan() {
		String op1 = stack.pop();
		String op2 = stack.pop();
		
		if ( checkNilDummy(op1) || checkNilDummy(op2) || 
				checkBoolean(op1) || checkBoolean(op2)){
			System.out.println("ls: illegal arguments");
		}
		
		if (op1.contains("tau::") | op2.contains("tau::") |
				op1.contains("STR:") | op2.contains("STR:") ){
			stack.push("<false>");
			return;
		}
		
		if (checkInt(op1) && checkInt(op2)){
			boolean result = getInt(op1) < getInt(op2);
			stack.push("<"+result+">");
		}else{
			System.out.println("ls: state should not be reached");
		}
	}
	
	private void equal() {
		String op1 = stack.pop();
		String op2 = stack.pop();
		
		if ( checkNilDummy(op1) || checkNilDummy(op2) || 
				checkBoolean(op1) || checkBoolean(op2)){
			System.out.println("eq: illegal arguments");
		}
		
		if ( op1.equals(op2)){
			stack.push("<true>");
		}else{
			stack.push("<false>");
		}
	}

	private void notEqual() {
		String op1 = stack.pop();
		String op2 = stack.pop();
	
		if ( checkNilDummy(op1) || checkNilDummy(op2) || 
				checkBoolean(op1) || checkBoolean(op2)){
			System.out.println("ne: illegal arguments");
		}
		
		if ( ! op1.equals(op2)){
			stack.push("<true>");
		}else{
			stack.push("<false>");
		}
	
	}

	private void plus() {
		String op1 = stack.pop();
		String op2 = stack.pop();
		if (checkInt(op1) && checkInt(op2)){
			int result = getInt(op1) + getInt(op2);
			stack.push("<INT:"+result+">");
		}else{
			System.out.println("+: integers operands expected");
		}
		
	}
	
	private void minus() {
		String op1 = stack.pop();
		String op2 = stack.pop();
		if (checkInt(op1) && checkInt(op2)){
			int result = getInt(op1) - getInt(op2);
			stack.push("<INT:"+result+">");
		}else{
			System.out.println("+: integers operands expected");
		}
	}
	
	private void multiply() {
		String op1 = stack.pop();
		String op2 = stack.pop();
		if (checkInt(op1) && checkInt(op2)){
			int result = getInt(op1) * getInt(op2);
			stack.push("<INT:"+result+">");
		}else{
			System.out.println("+: integers operands expected");
		}
		
	}

	private void devide() {
		String op1 = stack.pop();
		String op2 = stack.pop();
		if (checkInt(op1) && checkInt(op2)){
			int result = getInt(op1) / getInt(op2);
			stack.push("<INT:"+result+">");
		}else{
			System.out.println("+: integers operands expected");
		}
	}

	private void power() {
		String op1 = stack.pop();
		String op2 = stack.pop();
		if (checkInt(op1) && checkInt(op2)){
			int result = (int) Math.pow(getInt(op1),getInt(op2));
			stack.push("<INT:"+result+">");
		}else{
			System.out.println("+: integers operands expected");
		}
		
	}
	
	/*
	 * on the stack a tuple lives as <tau:#:a:b:c>
	 */
	private void aug() {
		String op1 = stack.pop();
		String op2 = stack.pop();
		// change to begin with
		
		StackTupleHandler op1Tuple = new StackTupleHandler(op1);
		op1Tuple.aug(op2);
		stack.push(op1Tuple.toString());
		/*
		if ( op1.contains("tau::")){
			String result = tupleAppend(op1,op2);
			stack.push(result);
		}else if (	op1.equals("<nil>") ){
			String result = "$$tau::1::"+op2+"::$$";
			stack.push(result);
		}else{
			System.out.println("aug: first operant must be an tuple");
		}*/
	}

	/*
	 * <tau::#$<STR:'fish'>::<INT:3>::c> aug (2,3) 
	 * Print( 1 + ('fish',3) 2)  
	 * tokens must hold on to their properties
	 */
	private String tupleAppend(String op1, String op2) {
		String tokens[] = op1.split("::"); 
		String order = tokens[1];
		order = "" + (Integer.parseInt(order) + 1);
		String result = "tau::" + order ;
		for(int i = 2; i < tokens.length ; i++){
			result += "::" + tokens[i];
		}
		result += "::" + op2;
		return result;
	}

	private void neg() {
		String op1 = stack.pop();
		if (checkInt(op1)){
			int result = (-1) * getInt(op1) ;
			stack.push("<INT:"+result+">");
		}else{
			System.out.println("+: integers operands expected");
		}
	}

	private void infix() {
		System.out.println("infix cant handle right now");
		System.exit(0);
		String op1 = stack.pop();
		String op2 = stack.pop();
		String op3 = stack.pop();
		if (checkInt(op1) && checkInt(op2)){
			int result = getInt(op1) + getInt(op2);
			stack.push("<INT:"+result+">");
		}else{
			System.out.println("+: integers operands expected");
		}
	}

	private int getInt(String op) {
		String intPart = op.split(":")[1];
		intPart = intPart.substring(0, intPart.length()-1);
		int result = Integer.parseInt(intPart);
		return result;
	}

	private boolean checkInt(String op1) {
		if (op1.contains("<INT:")){
			return true;
		}else{
			return false;
		}
	}
}
